package net.onceface.infra.domain;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import net.onceface.infra.spring.ioc.InstanceFactory;
import net.onceface.infra.util.Page;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

/**
 * 操作mongoDB数据库的父类，操作对象继承此类即可
 * 使用前需要先设置 mongoTemplate 在IOC的beanName 
 * --> setMongoTemplate(String beanName) 否则会默认使用 beanName = mongoTemplate
 * @author Derek Fung
 * **/
@SuppressWarnings("unchecked")
public class MongoBase<T> {

	private static final Logger LOGGER = LoggerFactory.getLogger(MongoBase.class);
	
	@SuppressWarnings({ "rawtypes" })
	private Class entityClass;
	
	private MongoTemplate mongoTemplate;
	
	public MongoTemplate getMongoTemplate(){
		if(mongoTemplate==null){
            //return InstanceFactory.getInstance(MongoTemplate.class, "mongoTemplate");
			setMongoTemplate("mongoTemplate");
        }
        return mongoTemplate;
	}
	
	/**
	 * 传入beanName 从IOC容器获取mongoTemplate
	 * **/
	public void setMongoTemplate(String beanName){
		mongoTemplate = InstanceFactory.getInstance(MongoTemplate.class, beanName);
	}
	
	@SuppressWarnings("rawtypes")
	public MongoBase() {
        Type genType = getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        entityClass = (Class) params[0];
    }
	
	/**
     * 保存一个对象
     * @param t
     * @return
     */
	public void save(T t){
		LOGGER.info("[Mongo Dao ]save:" + t);
		getMongoTemplate().save(t);
	}
	
	/**
     * 根据Id从Collection中查询对象
     * @param id
     *                 实体对象的Id,对应Collection中记录的_id字段. 
     *                 <p>
     *                     需要说明的是,Mongdo自身没有主键自增机制.解决方法
     *                     <ol>
     *                         <li>实体入库的时候,程序中为实体赋主键值.
     *                         <li>实体入库的时候,在mongodb中自定义函数实现主键自增机制.定义方法同js代码类似
     *                     </ol>
     *                 </p>
     * @return
     */
	public T queryById(String id){
		Query query = new Query();
        Criteria criteria = Criteria.where("id").is(id);
        query.addCriteria(criteria);
        LOGGER.info("[Mongo Dao ]queryById:" + query);
        return (T) getMongoTemplate().findOne(query, entityClass);
	}
	
	/**
     * 根据条件查询集合    
     * @param query        
     *                     查询条件
     * @return
     *                     满足条件的集合
     */
	public List<T> queryList(Query query){
		LOGGER.info("[Mongo Dao ]queryList:" + query);
        return getMongoTemplate().find(query, entityClass);
	}
	
	/**
     * 通过条件查询单个实体          
     * @param query
     * @return
     */
	public T queryOne(Query query){
		LOGGER.info("[Mongo Dao ]queryOne:" + query);
        return (T) getMongoTemplate().findOne(query, entityClass);
	}
	
	/**
     * 通过条件进行分页查询           
     * @param query
     *                     查询条件
     * @param p
     *                     分页  strat：开始数   size：条目数                  
     * @return
     *                     满足条件的集合
     */
	public Page<T> getPage(Query query, Page<T> p){
		query.skip((int)p.getStart());
        query.limit(p.getPageSize());
        LOGGER.info("[Mongo Dao ]queryPage:" + query + "(" + p.getStart() +"," + p.getPageSize() +")");
        List<T> lists = getMongoTemplate().find(query, entityClass);        
        long count = getMongoTemplate().count(query, entityClass);
        return new Page<T>(p.getStart(), count, lists);
	}
	
	 /**
     * 根据Id删除              
     * @param id
     */
	public void deleteById(String id){
		Criteria criteria = Criteria.where("_id").in(id);
        if(null != criteria){
            Query query = new Query(criteria);
            LOGGER.info("[Mongo Dao ]deleteById:" + query);
            if(null != query && this.queryOne(query) != null){
                this.deleteByQuery(query);
            }
        }
	}
	
	/**
	 * 按条件删除对象
	 * **/
	public void deleteByQuery(Query query){
		LOGGER.info("[Mongo Dao ]deleteByQuery:" + query);
		getMongoTemplate().remove(query, entityClass);
	}
	  /**
     * 删除对象             
     * @param t
     */
	public void delete(T t){
		 LOGGER.info("[Mongo Dao ]delete:" + t);
		 getMongoTemplate().remove(t);
	 }
	
	/**
     * 更新满足条件的第一个记录             
     * @param query
     * @param update
     */
	public void updateFirst(Query query,Update update){
		LOGGER.info("[Mongo Dao ]updateFirst:query(" + query + "),update(" + update + ")");
		getMongoTemplate().updateFirst(query, update, entityClass);
	}
	
	/**
     * 更新满足条件的所有记录              
     * @param query
     * @param update
     */
	public void updateMulti(Query query, Update update){
		LOGGER.info("[Mongo Dao ]updateMulti:query(" + query + "),update(" + update + ")");
		getMongoTemplate().updateMulti(query, update, entityClass);
	}
	
	/**
     * 查找更新,如果没有找到符合的记录,则将更新的记录插入库中               
     * @param query
     * @param update
     */
	public void updateInser(Query query, Update update){
		LOGGER.info("[Mongo Dao ]updateInser:query(" + query + "),update(" + update + ")");
        this.mongoTemplate.upsert(query, update, entityClass);
	}
}
